﻿using System;
using System.Data.Objects;
using System.Linq;

namespace Model
{
    public static class EntityExtensions
    {
        public static void AddObjectSafe(this ObjectSet<Rule_FxCopRule> objectSet, Rule_FxCopRule entity)
        {
            if (objectSet == null)
                throw new ArgumentNullException("objectSet");
            if (entity == null)
                throw new ArgumentNullException("entity");

            if (entity.Id == null)
                throw new ArgumentException("entity.Id cannot be null!", "entity");

            ActualEntities context = objectSet.Context as ActualEntities;
            if (context == null)
                throw new InvalidContextException();

            //if (context.Rules.Any(rule => rule.Id.Equals(entity.Id)))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a Rule entity in the context with the key " + entity.Id + " !");
            //if (context.Rule_FxCopRule.Any(rule => rule.Id.Equals(entity.Id)))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a Rule_FxCopRule entity in the context with the key " + entity.Id + " !");

            Rule r =
                new Rule()
                {
                    Id = entity.Id,
                    Rule_FxCopRule = entity
                };

            context.Rules.AddObject(r);
            context.Rule_FxCopRule.AddObject(entity);
            entity.Rule = r;
        }

        public static void AddObjectSafe(this ObjectSet<Category_Subcharacteristic> objectSet,
            Category_Subcharacteristic entity)
        {
            if (objectSet == null)
                throw new ArgumentNullException("objectSet");
            if (entity == null)
                throw new ArgumentNullException("entity");

            if (entity.Name == null)
                throw new ArgumentException("entity.Name cannot be null!", "entity");

            ActualEntities context = objectSet.Context as ActualEntities;
            if (context == null)
                throw new InvalidContextException();

            //if (context.Categories.Any(category => category.Name.Equals(entity.Name)))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a Category entity in the context with the key " + entity.Name + " !");
            //if (context.Category_Subcharacteristic.Any(subcharacteristic => subcharacteristic.Name.Equals(entity.Name)))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a Category_Subcharacteristic entity in the context with the key " + entity.Name + " !");

            Category c =
                new Category()
                {
                    Name = entity.Name,
                    Category_Subcharacteristic = entity
                };

            context.Categories.AddObject(c);
            context.Category_Subcharacteristic.AddObject(entity);
            entity.Category = c;
        }

        public static void AddObjectSafe(this ObjectSet<CallGraphEdge_AssemblyDependency> objectSet,
            CallGraphEdge_AssemblyDependency entity, string callType)
        {
            if (objectSet == null)
                throw new ArgumentNullException("objectSet");
            if (entity == null)
                throw new ArgumentNullException("entity");
            if (callType == null)
                throw new ArgumentNullException("callType");

            if (entity.Id == null)
                throw new ArgumentException("entity.Id cannot be null!", "entity");

            ActualEntities context = objectSet.Context as ActualEntities;
            if (context == null)
                throw new InvalidContextException();

            //if (context.CallGraphEdges.Any(edge => edge.Id == entity.Id))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge entity in the context with the key " + entity.Id + " !");
            //if (context.CallGraphEdge_AssemblyDependency.Any(edge => edge.Id == entity.Id))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge_AssemblyDependency entity in the context with the key "
            //        + entity.Id + " !");

            //context.SaveChanges();

            if (context.CallGraphEdge_AssemblyDependency.Where(
                    a => a.Callee_AssemblyName == entity.Callee_AssemblyName
                        && a.Caller_AssemblyName == entity.Caller_AssemblyName).Count() > 0)
                return;

            lock (context)
            {
                int id = 0;
                if (context.CallGraphEdges.Count() > 0)
                {
                    id = context.CallGraphEdges.Max(edge => edge.Id) + 1;
                }
                entity.Id = id;

                CallGraphEdge e =
                    new CallGraphEdge()
                    {
                        Id = entity.Id,
                        CallType = callType,
                        CallGraphEdge_AssemblyDependency = entity
                    };

                context.CallGraphEdges.AddObject(e);
                context.SaveChanges();
                //context.CallGraphEdge_AssemblyDependency.AddObject(entity);
                entity.CallGraphEdge = e;
                context.SaveChanges();
            }
        }

        public static void AddObjectSafe(this ObjectSet<CallGraphEdge_ModuleDependency> objectSet,
            CallGraphEdge_ModuleDependency entity, string callType)
        {
            if (objectSet == null)
                throw new ArgumentNullException("objectSet");
            if (entity == null)
                throw new ArgumentNullException("entity");
            if (callType == null)
                throw new ArgumentNullException("callType");

            if (entity.Id == null)
                throw new ArgumentException("entity.Id cannot be null!", "entity");

            ActualEntities context = objectSet.Context as ActualEntities;
            if (context == null)
                throw new InvalidContextException();

            //if (context.CallGraphEdges.Any(edge => edge.Id == entity.Id))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge entity in the context with the key " + entity.Id + " !");
            //if (context.CallGraphEdge_ModuleDependency.Any(edge => edge.Id == entity.Id))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge_ModuleDependency entity in the context with the key "
            //        + entity.Id + " !");

            if (context.CallGraphEdge_ModuleDependency.Where(
                    m => m.Callee_ModuleAssemblyName == entity.Callee_ModuleAssemblyName
                        && m.Callee_ModuleName == entity.Callee_ModuleName
                        && m.Caller_ModuleAssemblyName == entity.Caller_ModuleAssemblyName
                        && m.Caller_ModuleName == entity.Caller_ModuleName).Count() > 0)
                return;

            int id = 0;
            if (context.CallGraphEdges.Count() > 0)
            {
                id = context.CallGraphEdges.Max(edge => edge.Id) + 1;
            }
            entity.Id = id;

            CallGraphEdge e =
                new CallGraphEdge()
                {
                    Id = entity.Id,
                    CallType = callType,
                    CallGraphEdge_ModuleDependency = entity
                };

            context.CallGraphEdges.AddObject(e);
            context.SaveChanges();
            //context.CallGraphEdge_ModuleDependency.AddObject(entity);
            entity.CallGraphEdge = e;
            context.SaveChanges();
        }

        public static void AddObjectSafe(this ObjectSet<CallGraphEdge_NamespaceDependency> objectSet,
            CallGraphEdge_NamespaceDependency entity, string callType)
        {
            if (objectSet == null)
                throw new ArgumentNullException("objectSet");
            if (entity == null)
                throw new ArgumentNullException("entity");
            if (callType == null)
                throw new ArgumentNullException("callType");

            if (entity.Id == null)
                throw new ArgumentException("entity.Id cannot be null!", "entity");

            ActualEntities context = objectSet.Context as ActualEntities;
            if (context == null)
                throw new InvalidContextException();

            //if (context.CallGraphEdges.Any(edge => edge.Id == entity.Id))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge entity in the context with the key " + entity.Id + " !");
            //if (context.CallGraphEdge_NamespaceDependency.Any(edge => edge.Id == entity.Id))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge_NamespaceDependency entity in the context with the key "
            //        + entity.Id + " !");

            if (context.CallGraphEdge_NamespaceDependency.Where(
                n => n.Callee_NamespaceName == entity.Callee_NamespaceName
                    && n.Caller_NamespaceName == entity.Caller_NamespaceName).Count() > 0)
                return;

            int id = 0;
            if (context.CallGraphEdges.Count() > 0)
            {
                id = context.CallGraphEdges.Max(edge => edge.Id) + 1;
            }
            entity.Id = id;

            CallGraphEdge e =
                new CallGraphEdge()
                {
                    Id = entity.Id,
                    CallType = callType,
                    CallGraphEdge_NamespaceDependency = entity
                };

            context.CallGraphEdges.AddObject(e);
            context.SaveChanges();
            //context.CallGraphEdge_NamespaceDependency.AddObject(entity);
            entity.CallGraphEdge = e;
            context.SaveChanges();
        }

        public static void AddObjectSafe(this ObjectSet<CallGraphEdge_TypeDependency> objectSet,
            CallGraphEdge_TypeDependency entity, string callType)
        {
            if (objectSet == null)
                throw new ArgumentNullException("objectSet");
            if (entity == null)
                throw new ArgumentNullException("entity");
            if (callType == null)
                throw new ArgumentNullException("callType");

            if (entity.Id == null)
                throw new ArgumentException("entity.Id cannot be null!", "entity");

            ActualEntities context = objectSet.Context as ActualEntities;
            if (context == null)
                throw new InvalidContextException();

            //bool any = false;
            //foreach (var item in context.CallGraphEdges)
            //{
            //    any = (item.Id == entity.Id);
            //    if (any == true)
            //        break;
            //}
            //if (any == true)//(context.CallGraphEdges.Any(edge => edge.Id == entity.Id))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge entity in the context with the key " + entity.Id + " !");
            //if (context.CallGraphEdge_TypeDependency.Any(edge => edge.Id == entity.Id))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge_TypeDependency entity in the context with the key "
            //        + entity.Id + " !");

            if (context.CallGraphEdge_TypeDependency.Where(
                    t => t.Callee_TypeModuleAssemblyName == entity.Callee_TypeModuleAssemblyName
                        && t.Callee_TypeModuleName == entity.Callee_TypeModuleName
                        && t.Callee_TypeName == entity.Callee_TypeName
                        && t.Callee_TypeNamespaceName == entity.Callee_TypeNamespaceName
                        && t.Caller_TypeModuleAssemblyName == entity.Caller_TypeModuleAssemblyName
                        && t.Caller_TypeModuleName == entity.Caller_TypeModuleName
                        && t.Caller_TypeName == entity.Caller_TypeName).Count() > 0)
                return;

            int id = 0;
            if (context.CallGraphEdges.Count() > 0)
            {
                id = context.CallGraphEdges.Max(edge => edge.Id) + 1;
            }
            entity.Id = id;

            CallGraphEdge e =
                new CallGraphEdge()
                {
                    Id = entity.Id,
                    CallType = callType,
                    CallGraphEdge_TypeDependency = entity
                };

            context.CallGraphEdges.AddObject(e);
            context.SaveChanges();
            //context.CallGraphEdge_TypeDependency.AddObject(entity);
            entity.CallGraphEdge = e;
            context.SaveChanges();
        }

        public static void AddObjectSafe(this ObjectSet<CallGraphEdge_MemberDependency> objectSet,
            CallGraphEdge_MemberDependency entity, string callType)
        {
            if (objectSet == null)
                throw new ArgumentNullException("objectSet");
            if (entity == null)
                throw new ArgumentNullException("entity");
            if (callType == null)
                throw new ArgumentNullException("callType");

            if (entity.Id == null)
                throw new ArgumentException("entity.Id cannot be null!", "entity");

            ActualEntities context = objectSet.Context as ActualEntities;
            if (context == null)
                throw new InvalidContextException();

            //bool any = false;
            //foreach (var item in context.CallGraphEdges)
            //{
            //    any = (item.Id == entity.Id);
            //    if (any == true)
            //        break;
            //}
            //if (any == true)//context.CallGraphEdges.Any(edge => { return edge.Id == entity.Id; }))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge entity in the context with the key " + entity.Id + " !");
            //if (context.CallGraphEdge_MemberDependency.Any(edge => edge.Id == entity.Id))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a CallGraphEdge_MemberDependency entity in the context with the key "
            //        + entity.Id + " !");

            if (context.CallGraphEdge_MemberDependency.Where(
                    m => m.Callee_MemberName == entity.Callee_MemberName
                        && m.Callee_MemberTypeModuleAssemblyName == entity.Callee_MemberTypeModuleAssemblyName
                        && m.Callee_MemberTypeModuleName == entity.Callee_MemberTypeModuleName
                        && m.Callee_MemberTypeName == entity.Callee_MemberTypeName
                        && m.Callee_MemberTypeNamespaceName == entity.Callee_MemberTypeNamespaceName
                        && m.Caller_MemberName == entity.Caller_MemberName
                        && m.Caller_MemberTypeModuleAssemblyName == entity.Caller_MemberTypeModuleAssemblyName
                        && m.Caller_MemberTypeModuleName == entity.Caller_MemberTypeModuleName
                        && m.Caller_MemberTypeName == entity.Caller_MemberTypeName
                        && m.Caller_MemberTypeNamespaceName == entity.Caller_MemberTypeNamespaceName)
                            .Count() > 0)
                return;

            int id = 0;
            if (context.CallGraphEdges.Count() > 0)
            {
                id = context.CallGraphEdges.Max(edge => edge.Id) + 1;
            }
            entity.Id = id;

            CallGraphEdge e =
                new CallGraphEdge()
                {
                    Id = entity.Id,
                    CallType = callType,
                    CallGraphEdge_MemberDependency = entity
                };

            context.CallGraphEdges.AddObject(e);
            context.SaveChanges();
            //context.CallGraphEdge_MemberDependency.AddObject(entity);
            entity.CallGraphEdge = e;
            context.SaveChanges();
        }

        public static Message AddObjectSafe(this ObjectSet<Message_FxCopMessage> objectSet,
            Message_FxCopMessage entity, Rule rule, Assembly assembly, Module module,
            Namespace ns = null, Type type = null, Member member = null)
        {
            if (objectSet == null)
                throw new ArgumentNullException("objectSet");
            if (entity == null)
                throw new ArgumentNullException("entity");
            if (rule == null)
                throw new ArgumentNullException("rule");
            if (assembly == null)
                throw new ArgumentNullException("assembly");
            if (module == null)
                throw new ArgumentNullException("module");

            //if (entity.Id == null)
            //    throw new ArgumentException("entity.Id cannot be null!", "entity");

            ActualEntities context = objectSet.Context as ActualEntities;
            if (context == null)
                throw new InvalidContextException();

            //if (context.Messages.Any(message => message.Id.Equals(entity.MessageId)))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a Message entity in the context with the key " + entity.MessageId + " !");
            //if (context.Message_FxCopMessage.Any(message => message.MessageId.Equals(entity.MessageId)))
            //    throw new ObjectAlreadyAddedException(
            //        "There is a Message_FxCopMessage entity in the context with the key " + entity.MessageId + " !");

            int id = 0;
            if (context.Messages.Count() > 0)
            {
                id = context.Messages.Max(message => message.Id) + 1;
            }
            entity.MessageId = id;
            Message m =
                new Message()
                {
                    Id = id,
                    Assembly = assembly,
                    Member = member,
                    Message_FxCopMessage = entity,
                    Module = module,
                    Namespace = ns,
                    Rule = rule,
                    Type = type,
                };

            context.Messages.AddObject(m);
            context.SaveChanges();
            //context.Message_FxCopMessage.AddObject(entity);
            entity.Message = m;
            context.SaveChanges();
            return m;
        }

        [Obsolete("", true)]
        public static double GetWeight(this Message message)
        {
            double weight = 0;
            foreach (var mapping in message.Rule.Mappings)
            {
                if (mapping.Category.QualityProfileItem != null)
                    weight += mapping.Category.QualityProfileItem.Weight;
                //else weight += 0;
            }
            return weight / 100.0 * message.Message_FxCopMessage.FxCopIssues.First().Certainty;
        }
    }
}
